/**
 * @file xxx
 * @brief Declaration file of class xxx.
 *
 * This file includes the declaration of class xxx, and
 * the definitions of the macros, struct, enum and so on.
 *
 * @attention used for C++ only.
 */



#ifndef __cplusplus
#    error ERROR: This file requires C++ compilation (use a .cpp suffix)
#endif


#ifndef SIGNAL_H
#define SIGNAL_H


#include <list>
#include <algorithm>
#include "traits.h"

#define UNUSE(X) (void)(X)

template <typename Class, typename Method>
void impleDoTask(Class *c, Method m, std::tuple<> p)
{
    UNUSE(p);
    (c->*m)();
}

template <typename Class, typename Method, typename T0>
void impleDoTask(Class *c, Method m,  std::tuple<T0> p)
{
    (c->*m)(std::get<0>(p));
}
template <typename Class, typename Method, typename T0, typename T1>
void impleDoTask(Class *c, Method m,  std::tuple<T0, T1> p)
{
    (c->*m)(std::get<0>(p), std::get<1>(p));
}

template <typename Class, typename Method, typename T0, typename T1, typename T2>
void impleDoTask(Class *c, Method m,  std::tuple<T0, T1, T2> p)
{
    (c->*m)(std::get<0>(p), std::get<1>(p), std::get<2>(p));
}


template <typename Class, typename Method, typename T0, typename T1, typename T2, typename T3>
void impleDoTask(Class *c, Method m,  std::tuple<T0, T1, T2, T3> p)
{
    (c->*m)(std::get<0>(p), std::get<1>(p), std::get<2>(p), std::get<3>(p));
}

template <typename Class, typename Method, typename T0, typename T1, typename T2, typename T3, typename T4>
void impleDoTask(Class *c, Method m,  std::tuple<T0, T1, T2, T3, T4> p)
{
    (c->*m)(std::get<0>(p), std::get<1>(p), std::get<2>(p), std::get<3>(p), std::get<4>(p));
}

template <typename Class, typename Method, typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5>
void impleDoTask(Class *c, Method m,  std::tuple<T0, T1, T2, T3, T4, T5> p)
{
    (c->*m)(std::get<0>(p), std::get<1>(p), std::get<2>(p), std::get<3>(p), std::get<4>(p), std::get<5>(p));
}

template <typename Class, typename Method, typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6>
void impleDoTask(Class *c, Method m,  std::tuple<T0, T1, T2, T3, T4, T5, T6> p)
{
    (c->*m)(std::get<0>(p), std::get<1>(p), std::get<2>(p), std::get<3>(p), std::get<4>(p), std::get<5>(p),
            std::get<6>(p));
}

template <typename Class, typename Method, typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6, typename T7>
void impleDoTask(Class *c, Method m,  std::tuple<T0, T1, T2, T3, T4, T5, T6, T7> p)
{
    (c->*m)(std::get<0>(p), std::get<1>(p), std::get<2>(p), std::get<3>(p), std::get<4>(p), std::get<5>(p),
            std::get<6>(p), std::get<7>(p));
}

template <typename Class, typename Method, typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6, typename T7, typename T8>
void impleDoTask(Class *c, Method m,  std::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8> p)
{
    (c->*m)(std::get<0>(p), std::get<1>(p), std::get<2>(p), std::get<3>(p), std::get<4>(p), std::get<5>(p),
            std::get<6>(p), std::get<7>(p), std::get<8>(p));
}

template <typename Class, typename Method, typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6, typename T7, typename T8, typename T9>
void impleDoTask(Class *c, Method m,  std::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> p)
{
    (c->*m)(std::get<0>(p), std::get<1>(p), std::get<2>(p), std::get<3>(p), std::get<4>(p), std::get<5>(p),
            std::get<6>(p), std::get<7>(p), std::get<8>(p), std::get<9>(p));
}


template <typename Paras>
struct SlotBase
{
    virtual ~SlotBase() = 0;
    virtual void dotask(Paras paras) = 0;
};

template <typename Paras>
SlotBase<Paras>::~SlotBase()
{

}

template <typename M>
struct Slot:public SlotBase<typename ParameterTupleTraits<ParameterTraits<M> >::Parameters>
{

    typedef typename TypeTraits<typename ParameterTraits<M>::classType_value>::pointerType_value type_value;
    typedef typename ParameterTupleTraits<ParameterTraits<M> >::Parameters Parameters;
    Slot(type_value sloter, M method):m_object(sloter), m_method(method)
    {

    }

    void dotask(Parameters paras)
    {
        impleDoTask(m_object, m_method, paras);
    }

    type_value m_object;
    M m_method;
};

template <typename Method>
struct Signal
{
    ~Signal()
    {
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            delete (*ite);
        }
        sloters.clear();
    }

    template <typename M>
    bool connect(typename Slot<M>::type_value object, M method)
    {
        typename listValue_type::const_iterator ite = std::find_if(sloters.begin(), sloters.end(), FindHelper<M>(object, method));
        if (ite != sloters.end()) {
            return false;
        }
        sloters.push_back(new Slot<M>(object, method));
        return true;
    }

    template <typename M>
    bool disconnect(typename Slot<M>::type_value object, M method)
    {
        typename listValue_type::iterator ite = std::find_if(sloters.begin(), sloters.end(), FindHelper<M>(object, method));
        if (ite != sloters.end()) {
            delete (*ite);
            sloters.erase(ite);
            return true;
        }
        return false;
    }

    void eemit()
    {
        std::tuple<> para;
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    template <typename T0>
    void eemit(T0 t)
    {
        std::tuple<T0> para = std::make_tuple(t);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    template <typename T0, typename T1>
    void eemit(T0 t, T1 t1)
    {
        std::tuple<T0, T1> para = std::make_tuple(t, t1);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    template <typename T0, typename T1, typename T2>
    void eemit(T0 t, T1 t1, T2 t2)
    {
        std::tuple<T0, T1, T2> para = std::make_tuple(t, t1, t2);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }


    template <typename T0, typename T1, typename T2, typename T3>
    void eemit(T0 t, T1 t1, T2 t2, T3 t3)
    {
        std::tuple<T0, T1, T2, T3> para = std::make_tuple(t, t1, t2, t3);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    template <typename T0, typename T1, typename T2, typename T3, typename T4>
    void eemit(T0 t, T1 t1, T2 t2, T3 t3, T4 t4)
    {
        std::tuple<T0, T1, T2, T3, T4> para = std::make_tuple(t, t1, t2, t3, t4);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
    void eemit(T0 t, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
    {
        std::tuple<T0, T1, T2, T3, T4, T5> para = std::make_tuple(t, t1, t2, t3, t4, t5);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5,
              typename T6>
    void eemit(T0 t, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
    {
        std::tuple<T0, T1, T2, T3, T4, T5, T6> para = std::make_tuple(t, t1, t2, t3, t4, t5, t6);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5,
              typename T6, typename T7>
    void eemit(T0 t, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
    {
        std::tuple<T0, T1, T2, T3, T4, T5, T6, T7> para = std::make_tuple(t, t1, t2, t3, t4, t5, t6, t7);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5,
              typename T6, typename T7, typename T8>
    void eemit(T0 t, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
    {
        std::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8> para = std::make_tuple(t, t1, t2, t3, t4, t5, t6, t7, t8);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5,
              typename T6, typename T7, typename T8, typename T9>
    void eemit(T0 t, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9)
    {
        std::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> para = std::make_tuple(t, t1, t2, t3, t4, t5, t6, t7, t8, t9);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    //使用Method来实例化list，是为了在connect的时候，防止Method和M不一致，也就是参数不一致。
    typedef std::list<SlotBase<typename ParameterTupleTraits<ParameterTraits<Method> >::Parameters>* > listValue_type;
    listValue_type sloters;

    template <typename T>
    struct FindHelper
    {
        FindHelper(typename Slot<T>::type_value object, T method):m_object(object),m_method(method)
        {

        }

        bool operator()(const typename listValue_type::value_type& val) const
        {
            const Slot<T> *p = static_cast<Slot<T>*>(val);
            return (p->m_object == m_object) && (p->m_method == m_method);
        }

        bool operator()(typename listValue_type::value_type& val)
        {
            const Slot<T> *p = static_cast<Slot<T>*>(val);
            return (p->m_object == m_object) && (p->m_method == m_method);
        }

        typename Slot<T>::type_value m_object;
        T m_method;

    };


};

#endif // SIGNAL_H
